/**
 * Author:yuiop
 * Date:2022/3/29 17:07
 */
package com.devops.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.devops.admin.mapper.DOSRoleMapper;
import com.devops.admin.model.DOSMenu;
import com.devops.admin.model.DOSRole;
import com.devops.admin.model.DOSRoleMenuRelation;
import com.devops.admin.model.DOSRoleUserRelation;
import com.devops.admin.po.DOSRoleMenuRelationPo;
import com.devops.admin.po.DOSRolePo;
import com.devops.admin.service.DOSMenuService;
import com.devops.admin.service.DOSRoleMenuRelationService;
import com.devops.admin.service.DOSRoleService;
import com.devops.admin.service.DOSRoleUserRelationService;
import com.devops.admin.util.Assert;
import com.devops.admin.vo.DOSRoleVo;
import com.devops.common.utils.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class DOSRoleServiceImpl extends ServiceImpl<DOSRoleMapper, DOSRole> implements DOSRoleService {

    @Autowired
    private DOSRoleMenuRelationService dosRoleMenuRelationService;

    @Autowired
    private DOSRoleUserRelationService dosRoleUserRelationService;

    @Autowired
    private DOSMenuService dosMenuService;

    @Override
    public DOSRole saveRole(DOSRoleVo dosRole) {

        Assert.isTrue(StrUtil.isNotBlank(dosRole.getRoleName()),"角色名称不能为空");
        Assert.isTrue(StrUtil.isNotBlank(dosRole.getRoleCode()),"roleCode不能为空");
        Assert.isTrue(dosRole.getRoleLevel() != null && dosRole.getRoleLevel() > 0,"角色级别不能为空");

        Assert.isTrue(count(Wrappers.<DOSRole>lambdaQuery()
                .eq(DOSRole::getRoleName,dosRole.getRoleName())) == 0,"已存在相同名称的角色");

        DOSRole role = new DOSRole();
        BeanUtil.copyProperties(dosRole,role);
        role.setIsOperation(true);
        // 保存角色信息
        this.save(role);

        return role;
    }

    @Override
    public DOSRole updateRole(DOSRoleVo dosRole) {
        Assert.isTrue(dosRole.getId() != null && dosRole.getId() > 0,"角色id不能为空");
        // 判断角色是否允许被操作
        DOSRole role = this.getById(dosRole.getId());
        Assert.isTrue(role.getIsOperation(),"不允许操作该角色");

        DOSRole oldRole = this.getOne(Wrappers.<DOSRole>lambdaQuery().eq(DOSRole::getRoleName,dosRole.getRoleName()));
        if (oldRole != null && oldRole.getId() != null) {
            Assert.isTrue(oldRole.getId().intValue() == dosRole.getId().intValue(), "已存在需要更信息的角色");
        }
        DOSRole saveRole = new DOSRole();
        BeanUtil.copyProperties(dosRole,saveRole);

        this.updateById(saveRole);

        return this.getById(dosRole.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteRole(Integer roleId) {
        // 判断角色是否允许被操作
        DOSRole byId = this.getById(roleId);
        Assert.isTrue(byId.getIsOperation(),"不允许操作该角色");

        // 删除角色和菜单的关联
        LambdaQueryWrapper<DOSRoleMenuRelation> menuQueryWrapper = new LambdaQueryWrapper<>();
        menuQueryWrapper.eq(DOSRoleMenuRelation::getRoleId,roleId);
        dosRoleMenuRelationService.remove(menuQueryWrapper);

        // 删除角色与人员的关联
        LambdaQueryWrapper<DOSRoleUserRelation> userQueryWrapper = new LambdaQueryWrapper<>();
        userQueryWrapper.eq(DOSRoleUserRelation::getRoleId,roleId);
        dosRoleUserRelationService.remove(userQueryWrapper);

        // 删除角色信息
        return this.removeById(roleId);
    }

    @Override
    public Message bindMenus(DOSRoleVo roleVo) {
        // 判断角色是否允许被操作

        Assert.isTrue(roleVo.getId() != null && roleVo.getId() > 0,"角色id不能为空");
        Assert.isTrue(CollectionUtil.isNotEmpty(roleVo.getMenuIds()),"菜单id不能为空");

        // 查询当前节点和其子节点
        List<DOSMenu> nextMenus = dosMenuService.getTreeMenus(roleVo.getMenuIds());
        // 查询当前节点和其父节点
        List<DOSMenu> preMenus = dosMenuService.getPreTreeMenus(roleVo.getMenuIds());
        nextMenus.addAll(preMenus);
        // 去除重复数据
        List<DOSMenu> menus = nextMenus.stream().distinct().collect(Collectors.toList());

        LambdaQueryWrapper<DOSRoleMenuRelation> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DOSRoleMenuRelation::getRoleId,roleVo.getId());
        List<DOSRoleMenuRelation> dosRoleMenuRelations = dosRoleMenuRelationService.list(queryWrapper);

        List<Integer> newMenuIds = menus.stream().map(DOSMenu::getId).collect(Collectors.toList());
        List<DOSRoleMenuRelation> relations = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(dosRoleMenuRelations) && dosRoleMenuRelations.size() > 0) {
            List<Integer> oldMenuIds = dosRoleMenuRelations.stream().map(DOSRoleMenuRelation::getMenuId).collect(Collectors.toList());
            // 获取新的菜单id与旧的id的差集 - 保存差集的数据
            List<Integer> saveMenuIds = newMenuIds.stream().filter(id -> !oldMenuIds.contains(id)).collect(Collectors.toList());
            // 获取旧的菜单id与新的id的差集 - 删除差集的数据
            List<Integer> deleteMenuIds = oldMenuIds.stream().filter(id -> !newMenuIds.contains(id)).collect(Collectors.toList());
            // 封装角色与菜单的关系
            if (CollectionUtil.isNotEmpty(saveMenuIds)){
                for (Integer menuId : saveMenuIds) {
                    DOSRoleMenuRelation relation = DOSRoleMenuRelation.builder()
                            .roleId(roleVo.getId())
                            .menuId(menuId)
                            .build();
                    relations.add(relation);
                }
            }
            // 删除本次请求未选择的角色去菜单的关系
            if (CollectionUtil.isNotEmpty(deleteMenuIds)){
                LambdaQueryWrapper<DOSRoleMenuRelation> deleteQueryWrapper = new LambdaQueryWrapper<>();
                deleteQueryWrapper.eq(DOSRoleMenuRelation::getRoleId,roleVo.getId())
                        .in(DOSRoleMenuRelation::getMenuId,deleteMenuIds);

                dosRoleMenuRelationService.remove(deleteQueryWrapper);
            }
        }else{
            for (Integer newMenuId : newMenuIds) {
                DOSRoleMenuRelation relation = DOSRoleMenuRelation.builder()
                        .roleId(roleVo.getId())
                        .menuId(newMenuId)
                        .build();
                relations.add(relation);
            }
        }
        if (CollectionUtil.isNotEmpty(relations)) {
            // 保存新增的角色与菜单的关系
            dosRoleMenuRelationService.saveBatch(relations);
        }

        return Message.ok("绑定成功");
    }

    @Override
    public List<DOSRolePo> getAllRoles(DOSRoleVo roleVo) {

        List<DOSRolePo> dosRolePos = baseMapper.getAllRoles(roleVo);
        Map<Integer, List<DOSRolePo>> roleMap = dosRolePos.stream().collect(Collectors.groupingBy(DOSRolePo::getRoleId));
        List<DOSRolePo> rolePos = new ArrayList<>();
        roleMap.forEach((key,value) -> {
            DOSRolePo dosRolePo = value.get(0);
            List<DOSRoleMenuRelationPo> relations = new ArrayList<>();
            for (DOSRolePo rolePo : value) {
                DOSRoleMenuRelationPo relation = DOSRoleMenuRelationPo.builder()
                        .roleId(rolePo.getRoleId())
                        .menuId(rolePo.getMenuId())
                        .menuName(rolePo.getMenuName())
                        .build();
                relations.add(relation);
            }
            dosRolePo.setMenus(relations);
            rolePos.add(dosRolePo);
        });

        return rolePos.stream()
                .sorted(Comparator.comparing(DOSRolePo::getRoleLevel))
                .sorted(Comparator.comparing(DOSRolePo::getRoleId))
                .collect(Collectors.toList());
    }

    @Override
    public List<DOSRole> getUseable() {
        LambdaQueryWrapper<DOSRole> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DOSRole::getIsOperation,true);

        return this.list(queryWrapper);
    }
}
